package com.zzp.sms.security.provider;

import com.zzp.sms.security.service.UserDetailsServiceImpl;
import com.zzp.sms.security.token.PhoneNumAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
/**
 * @author 佐斯特勒
 * <p>
 *  电话认证提供者
 * </p>
 * @version v1.0.0
 * @date 2020/1/13 14:11
 * @see  DaoPhoneNumAuthenticationProvider
 **/
public class DaoPhoneNumAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsServiceImpl userDetailsService;

    public UserDetailsServiceImpl getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    /**
     * 对于手机号验证不用校验密码
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        var token = (PhoneNumAuthenticationToken) authentication;
        //获取手机号
        var phoneNum = (String) token.getPrincipal();

        //获取用户信息
        var userDetails = userDetailsService.loadUserByUsername(phoneNum);

        // 对于手机号登录，不用校验用户的密码，只用判断用户在不在
        if (null == userDetails) {
            throw new InternalAuthenticationServiceException(
                    "UserDetailsService returned null, which is an interface contract violation");
        }

        // 这是一个新的封装了用户的信息的token
        var phoneNumAuthenticationToken
                = new PhoneNumAuthenticationToken(userDetails, userDetails.getAuthorities());

        //将老的详情信息设置到新的token中
        phoneNumAuthenticationToken.setDetails(token.getDetails());

        return phoneNumAuthenticationToken;
    }

    /**
     * AuthenticationManager负责协调由那个Provider来处理对应的Filter, 具体协调的过程就是通过该方法来协调的.
     * 判断传入的参数 是否和 PhoneNumAuthenticationToken.class 为同一个类型。如果是同一个类型，就执行authenticate方法。
     *
     * @param authentication 认证
     * @return 是否同一个
     */
    @Override
    public boolean supports(Class<?> authentication) {
        /*
         * isAssignableFrom() 该方法是判断两个Class类型的对象是否为同一个。
         */
        return PhoneNumAuthenticationToken.class.isAssignableFrom(authentication);
    }

}
